package com.chaike.hsq.utils;

import com.chaike.common.ConstPool;
import com.chaike.hsq.exception.HuiShouQianRuntimeException;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.RegExUtils;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.URL;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Enumeration;

/**
 * 公私钥读取工具类.
 *
 * @author Luo
 * @version 1.0
 * @date 2021-10-14 10:36
 */
@Slf4j
@UtilityClass
public final class RsaReadUtil {

    private static final String HTTP_PREFIX = "http://";

    private static final String HTTPS_PREFIX = "https://";

    private static final String PROBLEM_MSG = "证书文件【%s】有问题，请核实！";

    private static final String NOT_FOUND_MSG = "证书文件【%s】不存在，请核实！";

    /**
     * 根据私钥路径读取私钥.
     *
     * @param pfxPath 私钥路径
     * @param pfxPwd  私钥密码
     * @return 私钥
     */
    public static PrivateKey getPrivateKeyFromFile(final String pfxPath, final String pfxPwd) {
        InputStream priKeyStream = null;
        try {
            priKeyStream = loadConfigInputStream(pfxPath);
            byte[] reads = new byte[priKeyStream.available()];
            priKeyStream.read(reads);
            return getPrivateKeyByStream(reads, pfxPwd);
        } catch (Exception e) {
            log.error("解析文件，读取私钥失败:", e);
        } finally {
            if (priKeyStream != null) {
                try {
                    priKeyStream.close();
                } catch (Exception e) {
                    //
                }
            }
        }
        return null;
    }

    /**
     * 根据PFX私钥字节流读取私钥.
     *
     * @param pfxBytes 私钥字节流
     * @param pfxPwd   私钥密码
     * @return 私钥
     */
    public static PrivateKey getPrivateKeyByStream(final byte[] pfxBytes, final String pfxPwd) {
        try {
            KeyStore ks = KeyStore.getInstance(RsaConsts.KEY_PKCS12);
            char[] charPriKeyPass = pfxPwd.toCharArray();
            ks.load(new ByteArrayInputStream(pfxBytes), charPriKeyPass);
            Enumeration<String> aliasEnum = ks.aliases();
            String keyAlias = null;
            if (aliasEnum.hasMoreElements()) {
                keyAlias = (String) aliasEnum.nextElement();
            }
            return (PrivateKey) ks.getKey(keyAlias, charPriKeyPass);
        } catch (IOException e) {
            // 加密失败
            log.error("解析文件，读取私钥失败:", e);
        } catch (KeyStoreException e) {
            log.error("私钥存储异常:", e);
        } catch (NoSuchAlgorithmException e) {
            log.error("不存在的解密算法:", e);
        } catch (CertificateException e) {
            log.error("证书异常:", e);
        } catch (UnrecoverableKeyException e) {
            log.error("不可恢复的秘钥异常", e);
        }
        return null;
    }

    /**
     * 根据公钥文件路径读取公钥.
     *
     * @param cerPath 公钥文件路径
     * @return 公钥
     */
    public static PublicKey getPublicKeyFromFile(final String cerPath) {
        InputStream pubKeyStream = null;
        try {
            pubKeyStream = loadConfigInputStream(cerPath);
            byte[] reads = new byte[pubKeyStream.available()];
            pubKeyStream.read(reads);
            return getPublicKeyByText(new String(reads));
        } catch (FileNotFoundException e) {
            log.error("公钥文件不存在:", e);
        } catch (IOException e) {
            log.error("公钥文件读取失败:", e);
        } finally {
            if (pubKeyStream != null) {
                try {
                    pubKeyStream.close();
                } catch (Exception e) {
                    //
                }
            }
        }
        return null;
    }

    /**
     * 根据公钥文本串读取公钥.
     *
     * @param pubKeyText 公钥文本串
     * @return 公钥
     */
    public static PublicKey getPublicKeyByText(final String pubKeyText) {
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance(RsaConsts.KEY_X509);
            BufferedReader br = new BufferedReader(new StringReader(pubKeyText));
            String line = null;
            StringBuilder keyBuffer = new StringBuilder();
            while ((line = br.readLine()) != null) {
                if (!line.startsWith("-")) {
                    keyBuffer.append(line);
                }
            }
            Base64 base64 = new Base64();
            Certificate certificate = certificateFactory.generateCertificate(new ByteArrayInputStream(base64.decode(keyBuffer.toString())));
            return certificate.getPublicKey();
        } catch (Exception e) {
            log.error("解析公钥内容失败:", e);
        }
        return null;
    }

    /**
     * 从配置路径 加载配置 信息（支持 classpath、本地路径、网络url）.
     *
     * @param configPath 配置路径
     * @return 文件输入流
     */
    private InputStream loadConfigInputStream(final String configPath) {
        InputStream inputStream;
        final String prefix = "classpath:";
        String fileHasProblemMsg = String.format(PROBLEM_MSG, configPath);
        String fileNotFoundMsg = String.format(NOT_FOUND_MSG, configPath);
        if (configPath.startsWith(prefix)) {
            String path = RegExUtils.removeFirst(configPath, prefix);
            if (!path.startsWith(ConstPool.SLASH)) {
                path = ConstPool.SLASH + path;
            }
            try {
                inputStream = ResourcesUtils.getResourceAsStream(path);
                if (inputStream == null) {
                    throw new HuiShouQianRuntimeException(fileNotFoundMsg);
                }
            } catch (Exception e) {
                throw new HuiShouQianRuntimeException(fileNotFoundMsg, e);
            }
        } else if (configPath.startsWith(HTTP_PREFIX) || configPath.startsWith(HTTPS_PREFIX)) {
            try {
                inputStream = new URL(configPath).openStream();
                if (inputStream == null) {
                    throw new HuiShouQianRuntimeException(fileNotFoundMsg);
                }
            } catch (IOException e) {
                throw new HuiShouQianRuntimeException(fileNotFoundMsg, e);
            }
        } else {
            try {
                File file = new File(configPath);
                if (!file.exists()) {
                    throw new HuiShouQianRuntimeException(fileNotFoundMsg);
                }
                inputStream = new FileInputStream(file);
            } catch (IOException e) {
                throw new HuiShouQianRuntimeException(fileHasProblemMsg, e);
            }
        }
        return inputStream;
    }

}
